package obecto.utility
{
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	
	import obecto.event.EventDescriptor;
	
	/**
	 * <code>Listener</code> provide us with a mechanism of binding an event 
	 * to a consumer function. Optionally we could pass additional data to that 
	 * function if the <code>Listener</code> is created as merging. This gives us 
	 * the power to pass data, that we've decided not to keep in the event object. 
	 * <p>
	 * In Actionscript 2 GuggaFF this additional data is actually merged into the 
	 * event object, but that approach is not working in Actionscript 3. In Actionscript 3
	 * dynamic addition of new properties is possible only for objects marked as 
	 * <i>dynamic</i> and that's the reason to pass this data as an additional argument to 
	 * the consumer function.
	 * <p>
	 * The correct use of the <code>Listener</code> is through its static methods:
	 * <ul>
	 * 	<li>create()</li>
	 * 	<li>createMergingListener()</li>
	 * 	<li>createSingleTimeListener()</li>
	 * 	<li>createSingleTimeMergingListener()</li>
	 * </ul>
	 */
	public class Listener
	{
		private var _dataObject : *;
		private var _consumerFunction : Function;
		private var _listenOnce : Boolean;
		private var _handleEventDelegate : Function;
		private var _targetEventDescriptor : EventDescriptor;
		private var _listening : Boolean;
		private var	_merging : Boolean;
		private var _useWeakReference : Boolean;
	
		public function get listening() : Boolean
		{
			return _listening;
		}
		
		public function get targetEventDescriptor() : EventDescriptor
		{
			return _targetEventDescriptor;
		}
		 
		public function Listener(
			aTargetEventDescriptor : EventDescriptor,
			consumerFunction : Function, 
			forwardOnce : Boolean = false,
			dataObject : * = undefined,
			merging : Boolean = false,
			useWeakReference : Boolean = false) 
		{
			_targetEventDescriptor = aTargetEventDescriptor
			_consumerFunction = consumerFunction;
			_dataObject = dataObject;
			_listenOnce = forwardOnce;
			_merging = merging;
			_useWeakReference = useWeakReference;
			
			_listening = false;
		}
		 
		public function start() : void
		{
			_listening = true;
			_targetEventDescriptor.eventSource.addEventListener(
				_targetEventDescriptor.eventName, 
				handleEvent, false, 0.0, _useWeakReference);
		}
		
		public function stop() : void
		{
			_listening = false;
			_targetEventDescriptor.eventSource.removeEventListener(
				_targetEventDescriptor.eventName, 
				handleEvent);
		}
		
		private function handleEvent(event : Event) : void
		{	
			if (_listenOnce)
			{
				this.stop();
			}
			
			if (_merging)
			(
				_consumerFunction(event, _dataObject)
			)
			else
			{
				_consumerFunction(event)
			}
		}
		
		public static function create(
			eventSource : IEventDispatcher,
			eventName : String, 
			consumerFunction : Function, 
			dataObject : Object, 
			forwardOnce : Boolean,
			useWeakReference : Boolean = false,
			startOnCreation : Boolean = true) : Listener
		{		
			var result : Listener = new Listener(
				EventDescriptor.create(eventSource, eventName),
				consumerFunction, 
 				forwardOnce,
				dataObject,
				false,
				useWeakReference);
			
			if (startOnCreation)
				result.start();
			
			return result;
		}	
		
		public static function createDataForwardingListener(
			eventSource : IEventDispatcher,
			eventName : String, 
			consumerFunction : Function, 
			dataObject : Object,
			useWeakReference : Boolean = false) : Listener
		{		
			var result : Listener = new Listener(
				EventDescriptor.create(eventSource, eventName),
				consumerFunction, 
 				false,
				dataObject,
				true,
				useWeakReference);
									
			result.start();
			
			return result;
		}	
		
		public static function createSingleTimeListener(
			eventSource : IEventDispatcher,
			eventName : String,
			consumerDelegate : Function, 
			useWeakReference : Boolean = false) : Listener
		{		
			var result : Listener = new Listener(
				EventDescriptor.create(eventSource, eventName),
				consumerDelegate,  
				true,
				null,
				false,
				useWeakReference);		
							
			result.start();
			
			return result;
		}	
	
		public static function createSingleTimeDataForwardingListener(
			eventSource : IEventDispatcher,
			eventName : String,
			consumerFunction : Function, 
			dataObject : Object, 
			useWeakReference : Boolean = false) : Listener
		{		
			var result : Listener = new Listener(
				EventDescriptor.create(eventSource, eventName),
				consumerFunction, 
				true,
				dataObject, 
				true,
				useWeakReference);
									
			result.start();
			
			return result;
		}
	}
}
